package util

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

var client = http.DefaultClient

type JMessageClient struct {
	domain string
}

type RegisterUserJson struct {
	Name string `json:"username"`
	Pwd  string `json:"password"`
}

type UpdateUserJson struct {
	NickName string `json:"nickname"`
}

type ChangePwdJson struct {
	Pwd string `json:"new_password"`
}

type TextMessageJson struct {
	Version     int                 `json:"version"`
	TargetType  string              `json:"target_type"`
	FromType    string              `json:"from_type"`
	MessageType string              `json:"msg_type"`
	TargetId    string              `json:"target_id"`
	FromId      string              `json:"from_id"`
	FromName    string              `json:"from_name"`
	Body        TextMessageBodyJson `json:"msg_body"`
}

type TextMessageBodyJson struct {
	Text   string            `json:"text"`
	Extras map[string]string `json:"extras"`
}

type CustomMessageJson struct {
	Version     int                   `json:"version"`
	TargetType  string                `json:"target_type"`
	FromType    string                `json:"from_type"`
	MessageType string                `json:"msg_type"`
	TargetId    string                `json:"target_id"`
	FromId      string                `json:"from_id"`
	FromName    string                `json:"from_name"`
	Body        CustomMessageBodyJson `json:"msg_body"`
}

type CustomMessageBodyJson struct {
	From     string `json:"from"`
	To       string `json:"to"`
	Id       int64  `json:"chat_log_id"`
	BodyType int    `json:"body_type"`
	Content  string `json:"body_content"`
}

type SendMessageRSJson struct {
	Id        int `json:"msg_id"`
	Timestamp int `json:"msg_ctime"`
}

type Message struct {
	Id       int64
	Body     []byte
	Callback func(int64, string) error
}

var prefix = ""
var queue = make(chan Message)
var domain = "https://api.im.jpush.cn"

func InitJms(runmode string) {
	if runmode == "dev" || runmode == "test" {
		prefix = runmode
	}

	go func() {
		for {
			doSend()
		}
	}()
}

func NewJMessageClient() JMessageClient {
	return JMessageClient{domain: domain}
}

func (cli JMessageClient) RegisterUser(name, pwd string) (ok bool) {
	url := fmt.Sprintf("%s/v1/users/", cli.domain)
	users := make([]RegisterUserJson, 1)
	users[0] = RegisterUserJson{Name: prefix + name, Pwd: pwd}
	if p, err := json.Marshal(users); err == nil {
		if resp, err := getResp("POST", url, bytes.NewBuffer(p)); err == nil {
			defer close(resp)
			if ok = resp.StatusCode == 201; !ok {
				p, _ := ioutil.ReadAll(resp.Body)
				GetLogger().Error("[jms.cli.RegisterUser] - error:%d|%s", resp.StatusCode, string(p))
			}
		} else {
			GetLogger().Error("[jms.cli.RegisterUser] - error:%s", err.Error())
		}
	}
	return
}

func (cli JMessageClient) ChangePwd(name, newPwd string) (ok bool) {
	url := fmt.Sprintf("%s/v1/users/%s/password", cli.domain, prefix+name)
	pwd := ChangePwdJson{Pwd: newPwd}
	if p, err := json.Marshal(pwd); err == nil {
		if resp, err := getResp("PUT", url, bytes.NewBuffer(p)); err == nil {
			defer close(resp)
			if ok = resp.StatusCode == 204; !ok {
				GetLogger().Error("[jms.cli.ChangePwd] - statuscode:%d", resp.StatusCode)
			}
		} else {
			GetLogger().Error("[jms.cli.ChangePwd] - error:%s", err.Error())
		}
	}
	return
}

func (cli JMessageClient) UpdateNickName(name, nickName string) (ok bool) {
	url := fmt.Sprintf("%s/v1/users/%s", cli.domain, prefix+name)
	usr := UpdateUserJson{NickName: nickName}
	if p, err := json.Marshal(usr); err == nil {
		if resp, err := getResp("PUT", url, bytes.NewBuffer(p)); err == nil {
			defer close(resp)
			if ok = resp.StatusCode == 204; !ok {
				GetLogger().Error("[jms.cli.UpdateNickName] - statuscode:%d", resp.StatusCode)
			}
		} else {
			GetLogger().Error("[jms.cli.UpdateNickName] - error:%s", err.Error())
		}
	}
	return
}

func (cli JMessageClient) DeleteUser(name string) (ok bool) {
	url := fmt.Sprintf("%s/v1/users/%s", cli.domain, prefix+name)
	if resp, err := getResp("DELETE", url, nil); err == nil {
		defer close(resp)
		if ok = resp.StatusCode == 204 || resp.StatusCode == 403; !ok {
			p, _ := ioutil.ReadAll(resp.Body)
			GetLogger().Error("[jms.cli.DeleteUser] - error:%d|%s", resp.StatusCode, string(p))
		}
	} else {
		GetLogger().Error("[jms.cli.DeleteUser] - error:%s", err.Error())
	}

	return
}

func (cli JMessageClient) UserExists(name string) (ok bool, err error) {
	url := fmt.Sprintf("%s/v1/users/%s", cli.domain, prefix+name)
	if resp, err := getResp("GET", url, nil); err == nil {
		defer close(resp)
		if ok = resp.StatusCode == 200; !ok {
			GetLogger().Error("[jms.cli.UserExists] - statuscode:%d", resp.StatusCode)
		}
	} else {
		GetLogger().Error("[jms.cli.UserExists] - error:%s", err.Error())
	}
	return
}

func (cli JMessageClient) SendCustomMessage(from, to, fromName, content string,
	mediaType int, chatLogId int64, callback func(int64, string) error) {
	message := CustomMessageJson{
		Version:     1,
		TargetType:  "single",
		TargetId:    prefix + to,
		FromType:    "admin",
		FromId:      "admin",
		FromName:    fromName,
		MessageType: "custom",
		Body: CustomMessageBodyJson{
			From:     from,
			To:       to,
			Id:       chatLogId,
			BodyType: mediaType,
			Content:  content,
		},
	}
	if p, err := json.Marshal(&message); err == nil {
		queue <- Message{Id: chatLogId, Callback: callback, Body: p}
	} else {
		GetLogger().Error("[jms.cli.SendMessage] - error:%s", err.Error())
	}
}

func (cli JMessageClient) SendTextMessage(from, to, fromName, text string,
	chatLogId int64, callback func(int64, string) error) {
	message := TextMessageJson{
		Version:     1,
		TargetType:  "single",
		TargetId:    prefix + to,
		FromType:    "admin",
		FromId:      "admin",
		FromName:    fromName,
		MessageType: "text",
		Body: TextMessageBodyJson{
			Text: text,
			Extras: map[string]string{
				"sender_id": prefix + from,
			},
		},
	}
	if p, err := json.Marshal(&message); err == nil {
		queue <- Message{Id: chatLogId, Callback: callback, Body: p}
	} else {
		GetLogger().Error("[jms.cli.SendMessage] - error:%s", err.Error())
	}
}

func getResp(method, url string, reader io.Reader) (resp *http.Response, err error) {
	var req *http.Request
	if req, err = http.NewRequest(method, url, reader); err == nil {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", fmt.Sprintf(
			"Basic %s", base64.StdEncoding.EncodeToString(
				[]byte(fmt.Sprintf("%s:%s", appKey, masterSecret)))))
		resp, err = client.Do(req)
	}
	return
}

func close(resp *http.Response) {
	if resp != nil && resp.Body != nil {
		resp.Body.Close()
	}
}

func doSend() {
	message := <-queue
	url := fmt.Sprintf("%s/v1/messages", domain)
	fmt.Println(string(message.Body))
	if resp, err := getResp("POST", url, bytes.NewBuffer(message.Body)); err == nil {
		defer close(resp)
		fmt.Println(resp.StatusCode)
		if ok := resp.StatusCode == 200 || resp.StatusCode == 201; ok {
			if p, err := ioutil.ReadAll(resp.Body); err == nil {
				result := &SendMessageRSJson{}
				if err := json.Unmarshal(p, result); err == nil {
					if err := message.Callback(message.Id, strconv.Itoa(result.Id)); err != nil {
						GetLogger().Error("[jms.cli.send] - error:%s", err.Error())
					}
				} else {
					GetLogger().Error("[jms.cli.send] - error:%s", err.Error())
				}
			} else {
				GetLogger().Error("[jms.cli.send] - error:%s", err.Error())
			}
		} else {
			GetLogger().Error("[jms.cli.send] - statuscode:%d", resp.StatusCode)
		}
	} else {
		GetLogger().Error("[jms.cli.send] - error:%s", err.Error())
	}
}
